package br.unb.statistic.distribution;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.HashMap;

import br.unb.utils.LogFile;
import br.unb.statistic.InterPacketTimeGenerator;
import br.unb.statistic.RandomGenerator;
import br.unb.statistic.SpectrumProcess;
import br.unb.statistic.function.HaarWavelet;
import br.unb.utils.LogPrinter;

public class WIGGenerator extends PacketsPerIntervalGenerator {

    //Parameters
    private long numSamples;
    private double hurst;
    private double variance;
    private double mean;
    //File
    private LogFile simulationFile;
    private LogPrinter simulationPrinter;
    private LineNumberReader lnr;

    public WIGGenerator() {
    }

    public WIGGenerator(String name, double mean, double variance, double hurst, long samples) throws IOException {
        initGen(name, mean, variance, hurst, samples, 1);
    }

    public WIGGenerator(String name, double mean, double variance, double hurst, long samples, double interval) throws IOException {
        initGen(name, mean, variance, hurst, samples, interval);
    }

    private void initGen(String name, double mean, double variance, double hurst, long samples, double interval) throws IOException {

        //Parameters
        this.hurst = hurst;
        this.mean = mean;
        this.interval = interval;
        this.variance = variance;
        this.numSamples = samples;
        //File
        this.simulationFile = new LogFile(LogFile.Type.SIMULATE, name);
        this.simulationPrinter = new LogPrinter(simulationFile);
        this.lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(this.simulationFile)));

        long steps = (long) Math.ceil(Math.log(samples) / Math.log(2));
        double[] variances = calculateWaveletVariances(steps);
        simulation(variances, steps);
    }

    private void simulation(double[] variances, long steps) {
        double[] values = {mean * Math.pow(2, steps)};
        for (int i = 0; i < steps; i++) {
            double[] result = upScale(variances[i], values);
            values = result;
        }
        for (int i = 0; i < numSamples; i++) {
            this.simulationPrinter.write(Double.toString(values[i]));
        }
    }

    private double[] calculateWaveletVariances(long steps) {
        double[] variances = new double[(int) steps];
        for (int j = 0; j < steps; j++) {
            variances[j] = variance * Math.pow(2, (2 * hurst - 1) * ((-1 * j) - 1)) * (2 - Math.pow(2, 2 * hurst - 1));
        }
        return variances;
    }

    private double[] upScale(double scaleVariance, double[] values) {
        NormalDistribution normal = new NormalDistribution(0, Math.sqrt(scaleVariance));
        double[][] aux = new double[2][values.length];
        aux[0] = values;
        for (int i = 0; i < values.length; i++) {
            aux[1][i] = normal.rand();
        }
        return HaarWavelet.upScale(aux);
    }

    public double rand() {
        try {
            return Double.parseDouble(lnr.readLine());
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Double.NaN;
    }

    public void setParameters(HashMap parameters) {
        // TODO Auto-generated method stub
    }

    /*
    public static void main(String args[]) {
    try {
    WIGGenerator gen = new WIGGenerator("testeWig", 100, 10, 0.85, 100000);
    gen.rand();
    } catch (IOException e) {
    e.printStackTrace();
    }

    }*/
}
